/* 
Пример показывает, как из Cи вызывать функцию, написанную
на ассемблере 

CodeVisionAVR C Compiler
(C) 2000-2002 HP InfoTech S.R.L.
www.hpinfotech.ro 
*/

// функция объявляется в ассемблере
// эта функция возвращает a+b+c
#pragma warn- // это запретит предупреждения

int sum_abc(int a, int b, unsigned char c) 
{
  #asm
    ldd r30,y+3   ;R30=LSB a
    ldd r31,y+4   ;R31=MSB a
    ldd r26,y+1   ;R26=LSB b
    ldd r27,y+2   ;R27=MSB b
    add r30,r26   ;(R31,R30)=a+b
    adc r31,r27
    ld r26,y      ;R26=c
    clr r27       ;преобразуем unsigned char c в int
    add r30,r26   ;(R31,R30)=(R31,R30)+c
    adc r31,r27
  #endasm
}

#pragma warn+ // разрешим предупреждения

void main(void) 
{
  int r;

  // теперь вызовем функцию и сохраним результат в r
  r=sum_abc(2,4,6);
}

/*
  Некоторые пояснения:
  Компилятор передает функциональные параметры, используя Data Stack
  (Стек Данных). Сначала он помещает целый (integer) параметр a, затем
  b, и, наконец, символьный без знака (unsigned char) параметр c.
  При каждом помещении параметра в стек парный регистра Y
  декрементируется (уменьшается) в зависимости от размера параметра
  (на 4 для long int (длинное целое), на 2 для int (целое), на 1 для
  char (символ)). Для многобайтовых параметров сначала помещается
  MSB (Старший Байт). Как видно, Стек Данных растет вниз.
  После того, как все параметры функций будут помещены в Стек Данных,
  регистр Y указывает на последний параметр c, так что мы можем
  прочитать его значение в R26, используя инструкцию: ld r26,y.
  Параметр b был помещен перед c, так что он находится по более
  старшему адресу в Стеке Данных.
  Мы прочитаем его, используя инструкции: ldd r27,y+2 (MSB) и 
  ldd r26,y+1 (LSB).
  MSB был помещен первым, так что он - по более старшему адресу.
  Параметр a был помещен перед b, так что он находится по еще более
  старшему адресу в Стеке Данных.
  Мы прочитаем его, используя инструкции: ldd r31,y+4 (MSB) и 
  ldd r30,y+3 (LSB).
  Функции возвращают свои значения в регистры:
    R30 для char и unsigned char
    R30, R31 для int и unsigned int
    R30, R31, R22, R23 для long и unsigned long.
  Итак, наша функция должна возвращать свой результат в регистры R30,
  R31.
  После возврата из функции, компилятор автоматически сгенерирует код,
  чтобы восстановить пространство Стека Данных, использованное
  параметрами функции, так что Вы не должны об этом беспокоиться.
  Директива компилятора #pragma warn- запрещает компилятору
  генерировать предупреждения о том, что наша функция не возвращает
  значение.
  Это необходимо, поскольку компилятор не знает того, что мы делаем в
  нашей ассемблерной части функции.
*/
